﻿using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ComponentModel;

using SharpGL.Media3D;
using SharpGL.SceneGraph.Helpers;

namespace SharpGL.SceneGraph.Primitives
{
    /// <summary>
	/// a polyline contains a set of vectors.
	/// </summary>
    [DataContract(IsReference = true)]
    public class Polyline : SceneElement, IRenderable, IFreezable, IVolumeBound, IDeepCloneable<Polyline>
    {
        #region Private Fields

        /// <summary>
        /// The freezable helper.
        /// </summary>
        private FreezableHelper freezableHelper = new FreezableHelper();
        //--------------------------------------------------------------------------------

        /// <summary>
        /// The bounding volume helper - used to ease implementation of IBoundVolume.
        /// </summary>
        private BoundingVolumeHelper boundingVolumeHelper = new BoundingVolumeHelper();
        //--------------------------------------------------------------------------------

        /// <summary>
        /// The vectors that make up the polyline.
        /// </summary>
        [DataMember]
        private List<Point3D> vertices = new List<Point3D>();
        //--------------------------------------------------------------------------------

        /// <summary>
        /// The vectors that make up the polyline.
        /// </summary>
        [DataMember]
        private bool generateNormals = false;
        //--------------------------------------------------------------------------------

        #endregion Private Fields

        #region Protected Methods

        /// <summary>
        /// Perform all rendering operation to the provided instance of OpenGL.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        /// <param name="renderMode">The render mode.</param>
        protected virtual void InternalRender(OpenGL gl, RenderMode renderMode)
        {
            if (OpenGL.IsValidContext(gl))
            {
                if (vertices != null && vertices.Count >= 2)
                {
                    // Begin drawing a polyline.
                    gl.Begin(OpenGL.GL_LINE_STRIP);
                    for (var vertexIndex = 0; vertexIndex < vertices.Count; vertexIndex++)
                    {
                        if (GenerateNormals && (vertexIndex < (vertices.Count - 1)))
                        {
                            var normal = vertices[vertexIndex + 1] - vertices[vertexIndex];
                            normal.Normalize();
                            gl.Normal(normal);
                        }

                        // Set the point.
                        gl.Vertex(vertices[vertexIndex]);
                    }
                    gl.End();
                }
            }
        }
        //--------------------------------------------------------------------------------

        #endregion Protected Methods

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Polyline"/> class.
        /// </summary>
        public Polyline()
        {
            Name = "Polyline";
        }
        //--------------------------------------------------------------------------------

        #endregion Constructors

        #region Public Methods

        /// <summary>
        /// Render to the provided instance of OpenGL.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        /// <param name="renderMode">The render mode.</param>
        public virtual void Render(OpenGL gl, RenderMode renderMode)
        {
            // If we're frozen, use the helper.
            if (freezableHelper.IsFrozen)
            {
                freezableHelper.Render();
            }
            else
            {
                InternalRender(gl, renderMode);
            }
        }
        //--------------------------------------------------------------------------------

		/// <summary>
        /// Freezes this instance using the provided OpenGL instance.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        public virtual void Freeze(OpenGL gl)
        {
            //  Freeze using the helper.
            freezableHelper.Freeze(gl, this);
        }
        //--------------------------------------------------------------------------------

        /// <summary>
        /// Unfreezes this instance using the provided OpenGL instance.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        public virtual void Unfreeze(OpenGL gl)
        {
            //  Unfreeze using the helper.
            freezableHelper.Unfreeze();
        }
        //--------------------------------------------------------------------------------

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// a new object that is a copy of this instance.
        /// </returns>
        public virtual Polyline DeepClone()
        {
            //  Create a new polygon.
            Polyline polyline = new Polyline();

            polyline.generateNormals = generateNormals;

            foreach(var vertex in vertices)
                polyline.vertices.Add(new Point3D(vertex));
            
            return polyline;
        }
        //--------------------------------------------------------------------------------

        #endregion Public Methods

        #region Public Properties

        /// Gets or sets the generate normals flag.
        /// </summary>
        /// <value>
        /// Generate normals flag.
        /// </value>
        [Description("Generate normals when rendering."), Category("Polyline")]
        public bool GenerateNormals
        {
            get { return generateNormals; }
            set
            {
                generateNormals = value;

                NotifyPropertyChanged("GenerateNormals");
            }
        }
        //--------------------------------------------------------------------------------

        /// <summary>
        /// Gets or sets the vectors.
        /// </summary>
        /// <value>
        /// The vectors.
        /// </value>
        [Description("The vectors that make up the polygon."), Category("Polyline")]
		public List<Point3D> Vertices
		{
            get { return vertices; }
            set
            {
                if (value != null)
                    vertices = value;
                else
                    vertices.Clear();

                NotifyPropertyChanged("Vertices");
            }
		}
        //--------------------------------------------------------------------------------

        /// <summary>
        /// Gets the bounding volume.
        /// </summary>
        [Browsable(false)]
        public BoundingVolume BoundingVolume
        {
            get 
            {
                // TODO: only create bv when vectors changed.
                boundingVolumeHelper.BoundingVolume.FromVertices(vertices);
                return boundingVolumeHelper.BoundingVolume;
            }
        }
        //--------------------------------------------------------------------------------

        /// <summary>
        /// Gets a value indicating whether this instance is frozen.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is frozen; otherwise, <c>false</c>.
        /// </value>
        [Browsable(false)]
        public virtual bool IsFrozen
        {
            get
            {
                return freezableHelper.IsFrozen;
            }
        }

        #endregion Public Properties
    }
}
